#ifndef __IRR_SMART_POINTER_H__
#define __IRR_SMART_POINTER_H__

#include <assert.h>
#include <stdexcept>
#include <memory>

#ifndef ASSERT
	#ifdef _DEBUG
		#include <assert.h>
		#define ASSERT(x) assert(x)
	#else
		#define ASSERT(x) 
	#endif
#endif

template<class T>
	class IrrSmartPointer;

template<class T>
struct IrrSmartPointerRef
{
	IrrSmartPointerRef(T* right) : _reference(right) { }
	T* _reference;
};

template<class T>
class IrrSmartPointer
{
private:
	T* _pointer;

public:
	explicit IrrSmartPointer(T* ptr = 0) : _pointer(0) {
		reset(ptr);
	}
	IrrSmartPointer(IrrSmartPointer<T>& right) : _pointer(0) {
		reset(right._pointer);
	}
	
	IrrSmartPointer(IrrSmartPointerRef<T> right) : _pointer(0) {
		T* ptr = right._reference;
		right._reference = 0;
		reset(ptr);
	}
  template<class OtherT>
	operator IrrSmartPointer<OtherT>() {
		return (IrrSmartPointer<OtherT>(*this));
	}
  template<class OtherT>
	operator IrrSmartPointerRef<OtherT>() {
		OtherT* ptr = _pointer;
		IrrSmartPointerRef<OtherT> result(ptr);
		//release();
		return result;
	}

  template<class OtherT>
	IrrSmartPointer<T>& operator=(IrrSmartPointer<OtherT>& right) {
		reset(right._pointer);
		return *this;
	}
  template<class OtherT>
	IrrSmartPointer(IrrSmartPointer<OtherT>& right) : _pointer(0) {
		reset(right._pointer);
	}
	IrrSmartPointer<T>& operator=(IrrSmartPointer<T>& right) {
		reset(right._pointer);
		return *this;
	}
	IrrSmartPointer<T>& operator=(IrrSmartPointerRef<T> right) {
		T* ptr = right._reference;
		right._reference = 0;
		reset(ptr);
		return *this;
	}
	~IrrSmartPointer() {
		release();
	}

	operator bool() {
		return get() != 0;
	}
	operator bool() const {
		return get() != 0;
	}
	operator T*() {
		return get();
	}
	
	IrrSmartPointer<T>& operator=(T* right) {
		reset(right);
		return *this;
	}

	T& operator*() const {
		ASSERT(_pointer && "pointer is null");
		return *get();
	}
	T* operator->() const {
		ASSERT(_pointer && "pointer is null");
		return get();
	}
	
	T* get() const{
		return _pointer;
	}
	T* release() {
		T* temp = _pointer;
		if (_pointer) {
			_pointer->drop();
		}
		_pointer = 0;
		return temp;
	}
	void setNewInstance(T* right) {
		reset(right);
		if (right) {
			right->drop();
		}
	}
	void reset(T* ptr = 0) {
		if (_pointer == ptr) {
			return;
		}
		if (_pointer) {
			_pointer->drop();
		}
		_pointer = ptr;
		if (_pointer) {
			_pointer->grab();
		}
	}
};

/*
template<class T>
bool operator&&(IrrSmartPointer<T>& left, bool right) {
	return left.get() != 0 && right;
}
template<class T>
bool operator&&(bool left, IrrSmartPointer<T>& right) {
	return left && right->get() != 0;
}
template<class T>
bool operator||(IrrSmartPointer<T>& left, bool right) {
	return left.get() != 0 || right;
}
template<class T>
bool operator||(bool left, IrrSmartPointer<T>& right) {
	return left || right->get() != 0;
}*/
#endif //__IRR_SMART_POINTER_H__